using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;

namespace Microsoft.SharedSource.SqlServer.MDDE.Components
{
    [
        System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")
    ]
    public abstract class MDDEContainer : MDDEComponent
    {
        #region Member Variables

        protected MDDEExecutionDAG innerExecutionDAG;        

        #endregion

        public MDDEContainer(Package package, Object component, string name, string uri)
            :
            base(package, component, name, uri)
        {
            if (!(component is DtsContainer))
                throw new ArgumentException();

            this.innerExecutionDAG = new MDDEExecutionDAG(this.package, this, (component as DtsContainer), uri);
        }

        #region Overridden Methods

        public override void GetChangeXml(MDDEComponent subject, XmlDocument changeXml)
        {
            throw new NotImplementedException();
        }

        public override bool CompareTo(MDDEComponent subject, bool deepCompare)
        {
            if (!(subject is MDDEContainer))
                return false;

            if (this.innerComponent.GetType() != subject.InnerComponent.GetType() || this.Name != subject.Name)
                return false;

            if (deepCompare && !this.innerExecutionDAG.CompareTo((subject as MDDEContainer).innerExecutionDAG, true))
                return false;

            return true;
        }

        public override MDDEComponent GetChildComponent(string uri)
        {
            if (null == this.innerExecutionDAG)
                return null;
            return innerExecutionDAG.GetMDDEComponentByUri(uri);
        }

        public override void GetDifference(MDDEComponent subject, XmlDocument differenceReport)
        {
            if (null == subject)
            {
                this.innerExecutionDAG.GetDifference(null, differenceReport);
            }
            else
            {
                if (!(subject is MDDEContainer))
                    throw new ArgumentException();

                this.innerExecutionDAG.GetDifference((subject as MDDEContainer).innerExecutionDAG, differenceReport);
            }
        }

        public override bool Validate()
        {
            try
            {
                if (((Executable)innerComponent).Validate(null, null, null, null)
                                    == DTSExecResult.Success)
                {
                    if (this.innerExecutionDAG.Validate())
                        return true;
                    else
                        return false;
                }
                else
                    return false;
            }
            
            catch
            {
                // TODO: Add to Log
                return false;
            }
        }

        public override XmlDocument GetComponentTree()
        {
            // Create a tree document.
            XmlDocument componentTree = new XmlDocument();

            // Append the root node.
            XmlElement xRoot = AppendChildElement(componentTree, "Node",
                new string[]{"Type", "Executable", "Name", Name, "Uri", Uri,
                    "ClassId", string.Empty, "RowsetDescriptorType", string.Empty,
                    "ContainsMappings", "False", "ContainsCollections", "False" });

            // Add the 'Event Handlers' node.
            XmlElement xEventHandlers = AppendChildElement(xRoot, "Node", 
                new string[]{ "Type", "Collection", "Name", "Event Handlers",
                    "Uri", Uri + "/Event Handlers", "ClassId", string.Empty, 
                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                    "ContainsCollections", "False" });

            // Add the 'Variables' node.
            XmlElement xVariables = AppendChildElement(xRoot, "Node",
                new string[]{ "Type", "Collection", "Name", "Variables",
                    "Uri", Uri + "/Variables", "ClassId", string.Empty,
                    "RowsetDescriptorType", string.Empty, "ContainsMappings", "False",
                    "ContainsCollections", "False" });

            // Traverse the execution DAG in execution order.
            foreach (MDDEComponent graphNode in this.innerExecutionDAG)
            {

                // Append the successor tree to appropriate parent node.

                // Check for Event Handlers.
                if (graphNode is MDDEEventHandler)
                {
                    // Get the component tree of node.
                    XmlDocument xSuccessorTree = graphNode.GetComponentTree();

                    // Append the child tree to 'Event Handlers' node.
                    xEventHandlers.AppendChild(
                        xRoot.OwnerDocument.ImportNode(
                        xSuccessorTree.DocumentElement, true));
                }

                // Check for Variables.
                if (graphNode is MDDEVariable)
                {
                    // Get the component tree of node.
                    XmlDocument xSuccessorTree = graphNode.GetComponentTree();

                    // Append the child tree to 'Variables' node.
                    xVariables.AppendChild(
                        xRoot.OwnerDocument.ImportNode(
                        xSuccessorTree.DocumentElement, true));
                }
            }

            // Append the properties
            base.AppendDtsProperties(
                xRoot, (IDTSPropertiesProvider)((DtsContainer)innerComponent));

            return componentTree;
        }

        #endregion

        #region Helper Methods

        protected object ConvertToCompatibleType(string value, string type)
        {
            object objValue = null;
            switch (type)
            {
                case "Boolean":
                    objValue = (object)(Convert.ToBoolean(value));
                    break;

                case "String":
                    objValue = (object)value;
                    break;

                case "DateTime":
                    objValue = (object)(Convert.ToDateTime(value));
                    break;

                case "Int32":
                    objValue = (object)(Convert.ToInt32(value));
                    break;
            }

            return objValue;
        }

        protected void UpdateNodeProperties(XmlDocument xmlDoc, XmlNode parentNode, string name, string value)
        {
            if (parentNode.ChildNodes.Count == 0)
                AddChildNode(xmlDoc, parentNode, "Properties");

            XmlNode propertiesNode = parentNode.ChildNodes[0];

            XmlNode propertyNode = AddChildNode(xmlDoc, propertiesNode, "Property");
            AddAttribute(xmlDoc, propertyNode, "Name", name);
            AddAttribute(xmlDoc, propertyNode, "Value", value);
        }

        protected XmlNode AddChildNode(XmlDocument xmlDoc, XmlNode parentNode, string name)
        {
            parentNode.AppendChild(xmlDoc.CreateNode(XmlNodeType.Element, name, null));
            return parentNode.LastChild;
        }

        protected void AddAttribute(XmlDocument xmlDoc, XmlNode parentNode, string name, string value)
        {
            XmlNode xmlAttrib = xmlDoc.CreateAttribute(name);
            xmlAttrib.InnerText = value;
            parentNode.Attributes.Append((XmlAttribute)xmlAttrib);
        }

        #endregion
    }
}
